package top.bulk.idgeneration.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import top.bulk.idgeneration.constant.Constants;
import top.bulk.idgeneration.entity.IdGeneratorEntity;
import top.bulk.idgeneration.entity.SegmentId;
import top.bulk.idgeneration.mapper.IdGeneratorMapper;
import top.bulk.idgeneration.service.IdGeneratorService;

import javax.annotation.Resource;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author 散装java
 * @date 2024-08-07
 */
@Service
@Slf4j
public class IdGeneratorServiceImpl implements IdGeneratorService {
    @Resource
    IdGeneratorMapper idGeneratorMapper;

    public SegmentId getNextSegmentId(Integer bizType) {
        // 获取nextTinyId的时候，有可能存在version冲突，需要重试
        for (int i = 0; i < Constants.RETRY; i++) {
            IdGeneratorEntity idGen = idGeneratorMapper.queryByBizType(bizType);
            if (idGen == null) {
                throw new RuntimeException("查不到业务场景");
            }
            Long newMaxId = idGen.getMaxId() + idGen.getStep();
            Long oldMaxId = idGen.getMaxId();
            int row = idGeneratorMapper.updateMaxId(idGen.getId(), newMaxId, oldMaxId, idGen.getVersion(),
                    idGen.getBizType());
            if (row == 1) {
                idGen.setMaxId(newMaxId);
                SegmentId segmentId = convert(idGen);
                log.info("getNextSegmentId 成功 idGen:{} current:{}", idGen, segmentId);
                return segmentId;
            } else {
                log.info("getNextSegmentId 冲突 idGen:{}", idGen);
            }
        }
        throw new RuntimeException("getNextSegmentId 获取号段重试后冲突");
    }

    public SegmentId convert(IdGeneratorEntity idInfo) {
        SegmentId segmentId = new SegmentId();
        segmentId.setCurrentId(new AtomicLong(idInfo.getMaxId() - idInfo.getStep()));
        segmentId.setMaxId(idInfo.getMaxId());
        // 默认20%加载下一号段
        segmentId.setLoadingId(segmentId.getCurrentId().get() + idInfo.getStep() * Constants.LOADING_PERCENT / 100);
        return segmentId;
    }
}
